# coding:utf-8

# 函数代码块以 def 关键词开头，后接函数标识符名称和圆括号()。
# 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
# 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
# 函数内容以冒号起始，并且缩进。
# return [表达式] 结束函数，选择性地返回一个值给调用方。不带表达式的return相当于返回 None。


def print_me(str1: object):
    # 打印传入的字符串
    print(str1)


print_me("调用函数！")


# python中strings,tuples,numbers是不可更改的对象，而list,dict是可以修改的对象
#   不可变类型：
#       变量赋值a=5后，再赋值a=10，这里实际是新生成了一个int值对象10，再让a指向它，而5被丢弃
#   可变类型：
#       变量赋值la=[1,2,3,4]后，在赋值la[2]=5则是将list la的第三个元素值更改，la本身没有动

#  python函数的参数传递
#   不可变类型：
#       类似c++的值传递，如整数、字符串、元组，fun(a)传递的只是a的值，没有影响a对象本身，比如在fun(a)
#       内部修改a的值，只是修改另一个复制的对象，不会影响a本身
#   可变类型：
#       类似c++的引用传递，如列表，字典，fun(la)则是将a真正的传递过去，修改后fun外部的la也会受影响

# python中一切都是对象，严格意义不能说是值传递还是引用传递，应该说传不可变对象和可变对象

# python传不可变对象实例
def change_int(a: int):
    print_me(a)
    a = 10
    # return a


b = 2
# b = change_int(b)  # 重新生成对象
change_int(b)
print_me(b)


# python传递可变对象实例
def change_list(al: list):
    al.append([1, 2, 3, 4])


li = [10, 20, 10, 30]
change_list(li)
print_me(li)


# 必备参数:必备参数须以正确的顺序传入函数，调用时的数量必须和声明时的一样
# 关键字参数：使用关键字参数允许在函数调用时参数的顺序与声明时不一致，因为python解析器能够用参数名匹配参数值
# 缺省参数：调用函数时，缺省参数的值如果没有传入，则被认为是默认值
# 不定长参数：加*的变领命会存放所有未命名的变量参数
def print_demo(str1: str, str2: str):
    print_me(str1)
    print_me(str2)


def print_default(str1, flag=True):
    if flag:
        print_me(str1)
    else:
        print_me("默认打印！")


def print_var(arg1: str, *_var: int):
    print_me(arg1)
    for var in _var:
        print_me("------" + str(var) + "-----")


print_demo(str2="2", str1="1")
print_default("传值打印！")
print_var(arg1="1")
print_var("a", 2, 3, 4)

# 匿名函数：python使用lambda创建匿名函数
#   lambda只是一个表达式，函数体比def简单很多
#   lambda的主体是一个表达式，而不是代码块，仅在lambda表达式中封装有限的逻辑
#   lambda函数拥有自己的命名空间，且不能访问自有参数列表之外或全局命名空间里的参数
#   虽然lambda函数看起来只能写一行，却不等同于c++的内联函数，后者的目的是调用小函数时不占用栈内存，从而增加运行效率


plus = lambda arg1, arg2: arg1 + arg2

print_me(plus(10, 20))
